package com.file.parser;

import com.file.bo.ResponseData;
import com.file.bo.mail.CMBC;
import com.file.bo.mail.CMBCTran;
import com.file.constant.ErrorCode;
import com.file.parser.traprange.PDFTableExtractor;
import com.file.util.JsonUtils;
import com.google.common.base.Strings;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import org.apache.pdfbox.text.TextPosition;
import technology.tabula.*;
import technology.tabula.detectors.NurminenDetectionAlgorithm;
import technology.tabula.extractors.BasicExtractionAlgorithm;

import java.io.File;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 民生银行pdf流水解析（表头字段不包含中英文, 交易字段包含"凭证类型"、"凭证号码"、"对方户名/账号"、"对方行名"、"交易机构"字段版本）
 * @author anyspa
 */

@Slf4j
public class CMBCPdfParser1 extends BasePdfParser {

    private static final Integer CMBC_PDF_HEADER_LINE_NUMBER = 3;
    private static final Integer CMBC_PDF_FOOTER_LINE_NUMBER = 2;

    public ResponseData<String> parseCMBCPdfToJson(String daId, String filePath) {
        log.info("parseCMBCPdfToJson1 started, daId:{}", daId);
        String json = null;

        try {
            CMBC cmbc = parseCMBCPdf(filePath);
            json = JsonUtils.convertObjectToJson(cmbc);
        } catch (Exception e) {
            log.error("OnError|{}||{}|{}|{}|{}", ErrorCode.FILE_PARSE_EXCEPTION.getCode(), daId, "", "", "parseCMBCPdfToJson1 failed", e);
            return new ResponseData<>(json, ErrorCode.FILE_PARSE_EXCEPTION.getCode(),
                    ErrorCode.FILE_PARSE_EXCEPTION.getMsg());
        }

        log.info("parseCMBCPdfToJson1 completed, daId:{}", daId);
        return new ResponseData<>(json, ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg());
    }

    private CMBC parseCMBCPdf(String filePath) {
        CMBC cmbc = parseCMBCHeader(filePath);

        List<CMBCTran> cmbcTrans = parseCmbcTrans(filePath);
        cmbc.setCmbcTrans(cmbcTrans);
        //log.info("cmbc = " + cmbc);
        return cmbc;
    }

    private CMBC parseCMBCHeader(String filePath) {
        CMBC cmbc = new CMBC();
        String pdfHeaderText = parsePdfHeaderText(filePath);
        String customerName = pdfHeaderText.substring(pdfHeaderText.indexOf("客户姓名:") + 5,
                pdfHeaderText.indexOf("客户账号")).trim();
        String customerAccount = pdfHeaderText.substring(pdfHeaderText.indexOf("客户账号:") + 5,
                pdfHeaderText.indexOf("产品名称")).trim();
        String productName = extractField(pdfHeaderText, "产品名称:", "币\\s*种");
        String currency = extractField(pdfHeaderText, "币\\s*种:", "钞汇标志");
//        String productName = pdfHeaderText.substring(pdfHeaderText.indexOf("产品名称:") + 5,
//                pdfHeaderText.indexOf("币    种")).trim();
//        String currency = pdfHeaderText.substring(pdfHeaderText.indexOf("币    种:") + 7,
//                pdfHeaderText.indexOf("钞汇标志")).trim();
        String cashExchange = pdfHeaderText.substring(pdfHeaderText.indexOf("钞汇标志:") + 5,
                pdfHeaderText.indexOf("开户机构")).trim();
        String accountOpeningInstitution = pdfHeaderText.substring(pdfHeaderText.indexOf("开户机构:") + 5,
                pdfHeaderText.indexOf("账户账号")).trim();
        String accountNo = pdfHeaderText.substring(pdfHeaderText.indexOf("账户账号:") + 5,
                pdfHeaderText.indexOf("起止日期")).trim();
        String transDetailPeriod = pdfHeaderText.substring(pdfHeaderText.indexOf("起止日期:") + 5,
                pdfHeaderText.indexOf("筛选条件")).trim();
        String filter = pdfHeaderText.substring(pdfHeaderText.indexOf("筛选条件:") + 5,
                pdfHeaderText.indexOf("证件号码")).trim();
        String idNo = pdfHeaderText.substring(pdfHeaderText.indexOf("证件号码:") + 5,
                pdfHeaderText.indexOf("凭证类型")).trim();

        cmbc.setCustomerName(customerName);
        cmbc.setCustomerAccount(customerAccount);
        cmbc.setProductName(productName);
        cmbc.setCurrency(currency);
        cmbc.setCashExchange(cashExchange);
        cmbc.setAccountOpeningInstitution(accountOpeningInstitution);
        cmbc.setAccountNo(accountNo);
        cmbc.setTransDetailPeriod(transDetailPeriod);
        cmbc.setFilter(filter);
        cmbc.setIdNo(idNo);
        return cmbc;
    }

    // 新增方法：使用正则表达式提取字段
    private String extractField(String text, String startPattern, String endPattern) {
        try {
            Pattern pattern = Pattern.compile(startPattern + "(.*?)" + endPattern);
            Matcher matcher = pattern.matcher(text);
            if (matcher.find()) {
                return matcher.group(1).trim();
            }
        } catch (Exception e) {
            log.error("extractField failed, startPattern: {}, endPattern: {}", startPattern, endPattern, e);
        }
        return "";
    }

    private List<CMBCTran> parseCmbcTrans(String filePath) {
        List<CMBCTran> cmbcTrans = new ArrayList<>();
//        // 替换默认的分隔符";"
//        String separator =  "~&#|~";
//        String transText = parseTransToText(filePath, separator);
//
//        if (Strings.isNullOrEmpty(transText)) {
//            return cmbcTrans;
//        }
//        List<List<String>> tranFieldsList = parseTransTextToTranFieldsList(transText, separator);
//        parseTranFieldsListToCMBCTrans(tranFieldsList, cmbcTrans);

        List<List<String>> rowList = parseFileToRowList(filePath);
        parseListToBO(rowList, cmbcTrans);
        return cmbcTrans;
    }

    private void parseListToBO(List<List<String>> rowList, List<CMBCTran> cmbcTrans) {
        StringBuilder certificateType = new StringBuilder();
        StringBuilder certificateNo = new StringBuilder();
        StringBuilder transactionTime = new StringBuilder();
        StringBuilder summary = new StringBuilder();
        StringBuilder transactionAmount = new StringBuilder();
        StringBuilder balance = new StringBuilder();
        StringBuilder cashTransfer = new StringBuilder();
        StringBuilder transactionChannel = new StringBuilder();
        StringBuilder transactionInstitution = new StringBuilder();
        StringBuilder counterPartyAccountName = new StringBuilder();
        StringBuilder counterBankName = new StringBuilder();

        // 每行的记录是否读取完成
        Boolean rowResult = false;
        for (int i = 0; i < rowList.size(); i++) {
            List<String> rows = rowList.get(i);
            List<String> nextStrings = new ArrayList<>();
            if (i < rowList.size() - 1) {
                nextStrings = rowList.get(i + 1);
            }

            if (rows.size() == 10) {
                certificateType.append(rows.get(0));
                certificateNo.append(rows.get(1));
                transactionTime.append(rows.get(2));
                summary.append(rows.get(3));
                transactionAmount.append(rows.get(4));
                String[] texts = rows.get(5).split(" ");
                if (texts.length == 2) {
                    balance.append(texts[0]);
                    cashTransfer.append(texts[1]);
                }
                transactionChannel.append(rows.get(6));
                transactionInstitution.append(rows.get(7));
                counterPartyAccountName.append(rows.get(8));
                counterBankName.append(rows.get(9));
            }

            if (nextStrings.isEmpty() || isDateTimeFormat(nextStrings.get(2))) {
                rowResult = true;
            }

            if (rowResult && StringUtils.isNotBlank(transactionTime)) {
                CMBCTran cmbcTran = new CMBCTran();
                cmbcTran.setCertificateType(certificateType.toString());
                cmbcTran.setCertificateNo(certificateNo.toString());
                cmbcTran.setTransactionTime(transactionTime.toString());
                cmbcTran.setSummary(summary.toString());
                cmbcTran.setTransactionAmount(transactionAmount.toString());
                cmbcTran.setBalance(balance.toString());
                cmbcTran.setCashTransfer(cashTransfer.toString());
                cmbcTran.setTransactionChannel(transactionChannel.toString());
                cmbcTran.setTransactionInstitution(transactionInstitution.toString());
                cmbcTran.setCounterPartyAccountName(counterPartyAccountName.toString());
                cmbcTran.setCounterBankName(counterBankName.toString());
                cmbcTrans.add(cmbcTran);

                certificateType.setLength(0);
                certificateNo.setLength(0);
                transactionTime.setLength(0);
                summary.setLength(0);
                transactionAmount.setLength(0);
                balance.setLength(0);
                cashTransfer.setLength(0);
                transactionChannel.setLength(0);
                transactionInstitution.setLength(0);
                counterPartyAccountName.setLength(0);
                counterBankName.setLength(0);
                rowResult = false;
            }
        }
    }

    private List<List<String>> parseFileToRowList(String filePath) {
        //页面所有table的记录，每一个元素代表下面一条记录cellList
        List<List<String>> rowList = new ArrayList<>();

        // 1. 读取文件
        File pdf = new File(filePath);

        // 2. pdfbox读取PDDocument
        try (PDDocument pdfDocument = PDDocument.load(pdf)) {

            // 3. tabula新建ObjectExtractor和NurminenDetectionAlgorithm，同时准备接收表格Rectangle的结构
            ObjectExtractor objectExtractor = new ObjectExtractor(pdfDocument);
            NurminenDetectionAlgorithm detectionAlgorithm = new NurminenDetectionAlgorithm();
            BasicExtractionAlgorithm bea = new BasicExtractionAlgorithm();

            // 4. 获取每页的PageIterator
            PageIterator pages = objectExtractor.extract();

            // 5. 解析每页的Rectangle(table的位置)
            while (pages.hasNext()) {
                Page page = pages.next();
                List<Rectangle> tablesOnPage = detectionAlgorithm.detect(page);
                if (tablesOnPage.size() > 0) {
                    Rectangle rectangle = tablesOnPage.get(0);
                    for (Rectangle r : tablesOnPage) {
                        if (r.getHeight() > 0) {
                            rectangle = r;
                            break;
                        }
                    }

                    if (rectangle != null) {
                        float top = findTextBottom(pdfDocument, page.getPageNumber(), "凭证类型");
                        rectangle.setTop(top);
                        rectangle.setRight((float) page.getWidth());
                        float bottom = findTextBottom(pdfDocument, page.getPageNumber(), "_______________________________________________________");
                        rectangle.setBottom(bottom - 10);

                        Page area = page.getArea(rectangle);
                        List<Table> tableList = null;
                        try {
                            tableList = bea.extract(area);
                        } catch (Exception e) {
                            log.info("rectangle extract table fail.");
                            continue;
                        }
                        for (Table table : tableList) {
                            for (int i = 0; i < table.getRowCount(); i++) {
                                List<String> cellList = new ArrayList<>();
                                for (int j = 0; j < table.getColCount(); j++) {
                                    cellList.add(table.getCell(i, j).getText(false));
                                }
                                rowList.add(cellList);
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return rowList;
    }

    // 校验字符串是否为日期格式
    private static boolean isDateTimeFormat(String str) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
        try {
            LocalDateTime.parse(str, formatter);
            return true;
        } catch (DateTimeParseException e) {
            return false;
        }
    }

    private static float findTextBottom(PDDocument document, int pageIndex, String searchText) throws IOException {
        float[] y = new float[1];
        PDFTextStripper stripper = new PDFTextStripper() {
            @Override
            protected void writeString(String text, List<TextPosition> textPositions) {
                StringBuilder sb = new StringBuilder();
                for (TextPosition tp : textPositions) {
                    sb.append(tp.getUnicode());
                }
                if (sb.toString().contains(searchText)) {
                    TextPosition tp = textPositions.get(0);
                    y[0] = tp.getY();
                }
            }
        };
        stripper.setSortByPosition(true);
        stripper.setStartPage(pageIndex);
        stripper.setEndPage(pageIndex);
        stripper.getText(document);
        return y[0];
    }

    private void parseTranFieldsListToCMBCTrans(List<List<String>> tranFieldsList, List<CMBCTran> cmbcTrans) {
        StringBuilder certificateType = new StringBuilder();
        StringBuilder certificateNo = new StringBuilder();
        StringBuilder transactionTime = new StringBuilder();
        StringBuilder summary = new StringBuilder();
        StringBuilder transactionAmount = new StringBuilder();
        StringBuilder balance = new StringBuilder();
        StringBuilder cashTransfer = new StringBuilder();
        StringBuilder transactionChannel = new StringBuilder();
        StringBuilder transactionInstitution = new StringBuilder();
        StringBuilder counterPartyAccountName = new StringBuilder();
        StringBuilder counterBankName = new StringBuilder();

        for (int i = 0; i < tranFieldsList.size(); i++) {
            if (Objects.nonNull(tranFieldsList.get(i)) && !tranFieldsList.get(i).isEmpty()) {
                if (StringUtils.isNotBlank(tranFieldsList.get(i).get(0)) && tranFieldsList.get(i).get(0).contains("凭证类型")) {
                    continue;
                }
                boolean isLastRowOfPreTran = checkIsLastRowOfPreTran(tranFieldsList, i + 1);
                if (isLastRowOfPreTran) {
                    CMBCTran cmbcTran = new CMBCTran();
                    cmbcTran.setCertificateType(certificateType.append(tranFieldsList.get(i).get(0)).toString());
                    cmbcTran.setCertificateNo(certificateNo.append(tranFieldsList.get(i).get(1)).toString());
                    cmbcTran.setTransactionTime(transactionTime.append(tranFieldsList.get(i).get(2)).toString());
                    cmbcTran.setSummary(summary.append(tranFieldsList.get(i).get(3)).toString());
                    cmbcTran.setTransactionAmount(transactionAmount.append(tranFieldsList.get(i).get(4)).toString());
                    cmbcTran.setBalance(balance.append(tranFieldsList.get(i).get(5)).toString());
                    cmbcTran.setCashTransfer(cashTransfer.append(tranFieldsList.get(i).get(6)).toString());
                    cmbcTran.setTransactionChannel(transactionChannel.append(tranFieldsList.get(i).get(7)).toString());
                    cmbcTran.setTransactionInstitution(transactionInstitution.append(tranFieldsList.get(i).get(8)).toString());
                    String counterPartyAccountNameStr = counterPartyAccountName.append(tranFieldsList.get(i).get(9)).toString();
                    if (counterPartyAccountNameStr.contains("/")
                            && !counterPartyAccountNameStr.matches("\\S+/[0-9a-zA-Z-]{3,}")) {
                        cmbcTran.setCounterPartyAccountName(formatCounterPartyAccountName(counterPartyAccountNameStr));
                    } else {
                        cmbcTran.setCounterPartyAccountName(counterPartyAccountNameStr);
                    }
                    cmbcTran.setCounterBankName(counterBankName.append(tranFieldsList.get(i).get(10)).toString());
                    cmbcTrans.add(cmbcTran);

                    certificateType = new StringBuilder();
                    certificateNo = new StringBuilder();
                    transactionTime = new StringBuilder();
                    summary = new StringBuilder();
                    transactionAmount = new StringBuilder();
                    balance = new StringBuilder();
                    cashTransfer = new StringBuilder();
                    transactionChannel = new StringBuilder();
                    transactionInstitution = new StringBuilder();
                    counterPartyAccountName = new StringBuilder();
                    counterBankName = new StringBuilder();
                } else {
                    certificateType.append(tranFieldsList.get(i).get(0));
                    certificateNo.append(tranFieldsList.get(i).get(1));
                    transactionTime.append(tranFieldsList.get(i).get(2));
                    summary.append(tranFieldsList.get(i).get(3));
                    transactionAmount.append(tranFieldsList.get(i).get(4));
                    balance.append(tranFieldsList.get(i).get(5));
                    cashTransfer.append(tranFieldsList.get(i).get(6));
                    transactionChannel.append(tranFieldsList.get(i).get(7));
                    transactionInstitution.append(tranFieldsList.get(i).get(8));
                    counterPartyAccountName.append(tranFieldsList.get(i).get(9));
                    counterBankName.append(tranFieldsList.get(i).get(10));
                }
            }
        }
    }

    private boolean checkIsLastRowOfPreTran(List<List<String>> tranFieldsList, int nextIndex) {
        if (nextIndex == tranFieldsList.size()) {
            return true;
        }
        return StringUtils.isNotBlank(tranFieldsList.get(nextIndex).get(2));
    }

    private String parseTransToText(String filePath) {
        PDFTableExtractor extractor = (new PDFTableExtractor()).setSource(filePath);
        int pdfPageNumber = getPdfPageNumber(filePath);

        // 处理 header
        for (int i = 0; i < pdfPageNumber; i++) {
            int[] headerSkipLines = new int[CMBC_PDF_HEADER_LINE_NUMBER];
            for (int k = 0; k < CMBC_PDF_HEADER_LINE_NUMBER; k++) {
                headerSkipLines[k] = k;
            }

            int[] footerSkipLines = new int[CMBC_PDF_FOOTER_LINE_NUMBER];
            for (int j = 0; j < CMBC_PDF_FOOTER_LINE_NUMBER; j++) {
                footerSkipLines[j] = -(j + 1);
            }
            extractor.exceptLine(i, headerSkipLines);
            extractor.exceptLine(i, footerSkipLines);
        }
        return extractPdfToText(extractor);
    }

    private String parseTransToText(String filePath, String separator) {
        PDFTableExtractor extractor = (new PDFTableExtractor()).setSource(filePath);
        int pdfPageNumber = getPdfPageNumber(filePath);

        // 处理 header
        for (int i = 0; i < pdfPageNumber; i++) {
            int[] headerSkipLines = new int[CMBC_PDF_HEADER_LINE_NUMBER];
            for (int k = 0; k < CMBC_PDF_HEADER_LINE_NUMBER; k++) {
                headerSkipLines[k] = k;
            }

            int[] footerSkipLines = new int[CMBC_PDF_FOOTER_LINE_NUMBER];
            for (int j = 0; j < CMBC_PDF_FOOTER_LINE_NUMBER; j++) {
                footerSkipLines[j] = -(j + 1);
            }
            extractor.exceptLine(i, headerSkipLines);
            extractor.exceptLine(i, footerSkipLines);
        }
        return extractPdfToText(extractor, separator);
    }

    private boolean isASCIIHalf(final char c) {
        return c >= 0x0020 && c <= 0x007E; //NOSONAR
    }

    private boolean isASCIIFull(final char c) {
        return (c >= 0xFF01 && c <= 0xFF5E) || c == 0x3000; //NOSONAR
    }

    private String formatCounterPartyAccountName(String counterPartyAccountName) {
        String formatStr = "";
        try {
            //平（安３银１行６电４子）商/1务50交00易08资97金44待32清5算专户
            // 电）商/7管51家22平10台12交65易01资00金13（01易久批
            int index = counterPartyAccountName.indexOf("/");
            StringBuilder upLineStringBuilder = new StringBuilder();
            StringBuilder downLineStringBuilder = new StringBuilder();

            for (int i = 0; i < counterPartyAccountName.length(); i++) {
                char c = counterPartyAccountName.charAt(i);
                if (isASCIIHalf(c)) {
                    downLineStringBuilder.append(c);
                } else if (isASCIIFull(c)) {
                    if (i < index) {
                        downLineStringBuilder.append(c);
                    } else {
                        upLineStringBuilder.append(c);
                    }
                } else {
                    upLineStringBuilder.append(c);
                }
            }
            formatStr = upLineStringBuilder.append(downLineStringBuilder).toString();
        } catch (Exception e) {
            log.error("formatCounterPartyAccountName failed, counterPartyAccountName: {}", counterPartyAccountName, e);
        }

        return formatStr;
    }

    public static void main(String[] args) throws IOException {
        CMBCPdfParser1 cmbcPdfParser = new CMBCPdfParser1();
        String json = cmbcPdfParser.parseCMBCPdfToJson("", "D:\\data\\files\\CMBC\\民生银行流水.pdf").getData();
        System.out.println(json);

        json = cmbcPdfParser.parseCMBCPdfToJson("", "D:\\data\\files\\CMBC\\zd4tdybm1547483479489552384_b5cdad2b11232f0a51ed1bef89cccb15_beehive-cmbc_jyls-0.pdf").getData();
        System.out.println(json);

        json = cmbcPdfParser.parseCMBCPdfToJson("", "D:\\data\\files\\CMBC\\zd4y7qpv1689843743362830336_090c6240d22693fc506a9fd932c714f2_beehive-cmbc_jyls-0.pdf").getData();
        System.out.println(json);

        json = cmbcPdfParser.parseCMBCPdfToJson("", "D:\\data\\files\\CMBC\\zd4y7qpv1689832815513055232_ea9cd0343d6f7e880bb79ceb833b1e2a_beehive-cmbc_jyls-0.pdf").getData();
        System.out.println(json);

        json = cmbcPdfParser.parseCMBCPdfToJson("", "D:\\data\\files\\CMBC\\zd4y7qpv1691361705845989376_fa111ac29bd9fd852d2e3e0cf62e09d8_beehive-cmbc_jyls-0.pdf").getData();
        System.out.println(json);

        json = cmbcPdfParser.parseCMBCPdfToJson("", "D:\\data\\files\\CMBC\\20250716174451958999994745603110023597999998535099.pdf").getData();
        System.out.println(json);

        json = cmbcPdfParser.parseCMBCPdfToJson("", "D:\\data\\files\\CMBC\\zd4gcm2j1950908842777030656_5342fd7080435a54b7aa7e3ca987fb9a_beehive-cmbc_jyls-0.pdf").getData();
        System.out.println(json);
    }
}

